package ComoViajo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;


/**
 * representacion de la red de transporte
 * 
 * @author eugenio
 * 
 */
public class RedDeTransporte {
	public ArrayList<ArrayList<Estacion>> ramales = new ArrayList<ArrayList<Estacion>>();

	private Estacion destino;
	
	private ArrayList<Viaje> losViajes; 	
	
	/**
	 * Dada una estacion encuentra su ramal
	 * @author eugenio
	 * @param estacion que buscamos
	 * @return el ramal es un arraylist de estaciones
	 * @throws BusquedaException en caso de que no encuentre nada
	 */
	public ArrayList<Estacion> getRamal(Estacion estacion) throws BusquedaException {
		Estacion laEstacion;
		ArrayList<Estacion> elRamal;

		Iterator<ArrayList<Estacion>> ramalesIterator = this.ramales.iterator();
		Iterator<Estacion> estacionesIterator;

		// encontrar el ramal y dejarlo en la variable ramal
		while (ramalesIterator.hasNext()) {
			elRamal = ramalesIterator.next();

			estacionesIterator = elRamal.iterator();
			while (estacionesIterator.hasNext()) {
				laEstacion = estacionesIterator.next();
				if (laEstacion.IsEqual(estacion)) {
					return elRamal;
				}
			}
		}

		// si no encontro el ramal
		throw new BusquedaException("no se encontro ramal");

	}
	
	/**
	 * tanto el origen como el destino pueden instansearse desde cualquier parte
	 * lo importante es que el nombre del ramal y el nombre de la estacion de
	 * destino sean conocidas por el pasajero
	 * 
	 * @author eugenio
	 * @param origen
	 * @param destino
	 * @return una collection de viajes, que tiene una coleccion de recorridos,
	 *         los recorridos son una collection de objetos Estacion
	 * @throws BusquedaException 
	 * @throws ComoViajeException 
	 * @throws Exception
	 */
	public ArrayList<Viaje> getViajes(Estacion estacionOrigen, Estacion estacionDestino) throws BusquedaException, ComoViajeException {
		this.destino = estacionDestino;
			
		this.losViajes = new ArrayList<Viaje>();
	
		Viaje elViaje=new Viaje();
		Recorrido recorrido=estacionOrigen.getEstacionesContainer();
		recorrido.estaciones.add(estacionOrigen);
		
		this.recorrerRecursivamente(elViaje, estacionOrigen, recorrido);
		
		return this.losViajes;
	}

	/**
	 * @author eugenio
	 * @param elViaje
	 * @param estacionEnQueNosEncontramos
	 * @param elRecorrido
	 * @throws BusquedaException
	 * @throws ComoViajeException
	 */
	private void recorrerRamalHaciaAdelante(Viaje elViaje, Estacion estacionEnQueNosEncontramos, Recorrido elRecorrido) throws BusquedaException, ComoViajeException{
		ListIterator<Estacion> ramalIterator=this.getRamalIteratorEnPosicion(estacionEnQueNosEncontramos);
		Estacion estacionSig;
		
		ramalIterator.next();
		
		while(ramalIterator.hasNext()){
			estacionSig=ramalIterator.next();
			
			elRecorrido.estaciones.add(estacionSig);
			
			if(this.siLlegamosAlDestinoGuardarViaje(estacionSig, elRecorrido, elViaje)){
				return;
			}
			
			this.recorrerEstacionesConectadas(elViaje.getCopia(), estacionSig,elRecorrido.getCopia());
			
		}
		
	}
	
	/**
	 * @author eugenio
	 * @param elViaje
	 * @param estacionEnQueNosEncontramos
	 * @param elRecorrido
	 * @throws BusquedaException
	 * @throws ComoViajeException
	 */
	private void recorrerRamalHaciaAtras(Viaje elViaje, Estacion estacionEnQueNosEncontramos, Recorrido elRecorrido) throws BusquedaException, ComoViajeException{
		ListIterator<Estacion> ramalIterator=this.getRamalIteratorEnPosicion(estacionEnQueNosEncontramos);
		Estacion estacionPrev;
		
		while(ramalIterator.hasPrevious()){
			estacionPrev=ramalIterator.previous();
			
			elRecorrido.estaciones.add(estacionPrev);
			
			// si llegamos al destino guardamos el recorrido en el viaje y el viaje en los viajes
			if(this.siLlegamosAlDestinoGuardarViaje(estacionPrev, elRecorrido, elViaje)){
				return;
			}
			
			this.recorrerEstacionesConectadas(elViaje.getCopia(), estacionPrev,elRecorrido.getCopia());
		}
		
	}
	
	/**
	 * @author eugenio
	 * @param elViaje
	 * @param estacionEnQueNosEncontramos
	 * @param elRecorrido
	 * @throws BusquedaException
	 * @throws ComoViajeException
	 */
	private void recorrerEstacionesConectadas(Viaje elViaje, Estacion estacionEnQueNosEncontramos, Recorrido elRecorrido) throws BusquedaException, ComoViajeException {		
		Iterator<Estacion> estacionesConectadasIterator=estacionEnQueNosEncontramos.estacionesConectadas.iterator();
		Estacion estacionConectada;
		Recorrido nuevoRecorrido;
		Viaje nuevoViaje;
		
		while(estacionesConectadasIterator.hasNext()){
			estacionConectada=estacionesConectadasIterator.next();
						
			nuevoViaje=elViaje.getCopia();
			nuevoViaje.recorridos.add(elRecorrido.getCopia());
			nuevoRecorrido=estacionConectada.getEstacionesContainer();
			nuevoRecorrido.estaciones.add(estacionConectada);
						
			this.recorrerRecursivamente(nuevoViaje,estacionConectada,nuevoRecorrido);
			
		}
		
	}
	
	private void recorrerRecursivamente(Viaje elViaje, Estacion estacionEnLaQueEstamos, Recorrido elRecorrido) throws BusquedaException, ComoViajeException {
		// si llegamos o tenemos el corte de control
		if((this.corteDeControlGetViajes(elViaje)) || (this.siLlegamosAlDestinoGuardarViaje(estacionEnLaQueEstamos, elRecorrido, elViaje))		
		){
			return;
		}
		
		ListIterator<Estacion> ramalIterator=getRamalIteratorEnPosicion(estacionEnLaQueEstamos);
		
		Estacion estacion;
				
		if(ramalIterator.hasNext()){
			estacion=ramalIterator.next();
			this.recorrerRamalHaciaAdelante(elViaje.getCopia(), estacion,elRecorrido.getCopia());
		}
		
		// para que si es la primera vez que entra la estacion origen sea el objeto
		// que conoce las estaciones conectadas
		estacionEnLaQueEstamos=ramalIterator.previous();
		
		if(ramalIterator.hasPrevious()){
			
			this.recorrerRamalHaciaAtras(elViaje.getCopia(), estacionEnLaQueEstamos,elRecorrido.getCopia());
		}
	
		this.recorrerEstacionesConectadas(elViaje.getCopia(),estacionEnLaQueEstamos,elRecorrido.getCopia());
		
	}

	/**
	 * @author eugenio
	 * @param elViaje
	 * @return true si se debe realizar el corte de control que recorre los viajes
	 */
	private boolean corteDeControlGetViajes(Viaje elViaje){
		// corte de control
		if(elViaje.recorridos.size()>1) return true;
		return false;
	}
	

	private boolean siLlegamosAlDestinoGuardarViaje(Estacion estacion,Recorrido recorrido, Viaje elViaje) {
		if(estacion.IsEqual(this.destino)){
			// llegamos, agregamos el recorrido al viaje			
			elViaje.recorridos.add(recorrido);

			// agregamos el viaje al array que tiene todos los viajes posibles
			this.losViajes.add(elViaje);
			
			return true;
		}
		return false;
	}

	private ListIterator<Estacion> getRamalIteratorEnPosicion(Estacion estacion) throws BusquedaException{
		List<Estacion> ramal = this.getRamal(estacion);				
		Estacion e;
		
		// crear iterator para recorrer las estaciones del ramal
		ListIterator<Estacion> estacionesRamalListIterator= ramal.listIterator();

		// poner el ramal en posicion para ser recorrido
		while (estacionesRamalListIterator.hasNext()){
			e=estacionesRamalListIterator.next();
			if(estacion.toString().equals(e.toString())) break;
		}
		estacionesRamalListIterator.previous();
		
		return estacionesRamalListIterator;
	}
}